package icasue.webdashboard.api;

import icasue.base.utils.wareable.FieldConvertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.logging.log4j.util.Strings;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import icasue.tools.compile.utils.DCUtil;
import icasue.reflect.handles.HandleSupplier;
import icasue.reflect.handles.arrays.AryOF;
import icasue.reflect.handles.classes.ClassOF;
import icasue.reflect.handles.fields.FieldOF;
import icasue.reflect.handles.method.MethodOF;
import icasue.reflect.handles.object.ObjectOF;
import icasue.runninginvoke.invoke.RunningInvoking;
import icasue.webdashboard.apptree.buffer.FileMappingBuffer;
import icasue.webdashboard.apptree.execute.ExecuteMetadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/12/9 上午11:33
 * @UpdateDate:
 * @Description:
 */
@RestController
@RequestMapping("/dashboard/tree")
public class AppClsDirMappingApi extends DashboardApiWare{

    @PostMapping("/mappings")
    public Object mappings(){
        return FieldOF.get_ofTp_fName_instance_.apply(FileMappingBuffer.class,"rootMapping",null);
    }

    @PostMapping("/clsApi")
    public Object clsApi(String clsMetadata) {
        Object cache = getCacheData.apply(clsMetadata);
        if(ObjectOF.notNull_.test(cache)) return cache;
        try {
            Method[] apply = ClassOF.getDeclaredMethods_.apply(Class.forName(clsMetadata));
            if(apply == null || apply.length == 0) return null;
            List<ExecuteMetadata> metadataArray = new ArrayList<>(apply.length);
            for (Method method : apply) {
                if(!filterMethod.test(method.getName(),clsMetadata)) continue;
                ExecuteMetadata clsMeta = new ExecuteMetadata(){{ setClassName(clsMetadata); setMethodName(method.getName()); }};
                Annotation runningInvokable;
                if(methodMarker != null && (runningInvokable = method.getAnnotation(methodMarker)) != null){
                    if(ObjectOF.notNull_.test(runningInvokable)){
                        String mDescription = (String) MethodOF.invoke_mType_mName_inst_paramArray.apply(methodMarker, "description", runningInvokable, null);
                        String[] pDocs = (String[]) MethodOF.invoke_mType_mName_inst_paramArray.apply(methodMarker, "docs", runningInvokable, null);
                        clsMeta.setFunctionDescription(mDescription);
                        List<ExecuteMetadata.ParamMetadata> params = digestParams(method.getParameters(), pDocs);
                        clsMeta.setParams(params);
                    }
                }else {
                    List<ExecuteMetadata.ParamMetadata> params = digestParams(method.getParameters(), null);
                    clsMeta.setParams(params);
                }
                metadataArray.add(clsMeta);
            }
            setCacheData.accept(clsMetadata,metadataArray);
            return metadataArray;
        }catch (Throwable e){ return null; }
    }

    @PostMapping("/execute")
    public Object execute(@RequestBody ExecuteMetadata clsMetadata){
        //参数转换 (JSON / JSONArray / Array / List / SpecialModel) > Object > BaseType.
        if(clsMetadata.getParams() == null) clsMetadata.setParams(Collections.EMPTY_LIST);
        for (ExecuteMetadata.ParamMetadata param : clsMetadata.getParams()) {
            Class<?> paramCls = null;
            try { paramCls = DCUtil.loadClass(param.getParamClassName()); }catch (Throwable e){
                try { paramCls = Class.forName(param.getParamClassName()); }catch (Throwable e2){
                    return LOCATE_CLASS_ERROR; /*实际这里不会出现反射异常, 除非从浏览器修改HTML元素内容*/
                }
            }
            if(ObjectOF.notNull_.test(param.getParamValue()) &&
                    !ObjectOF.equals_.test(param.getParamClassName(),String.class.getName())){
                String paramVal = param.getParamValue().toString();
                String paramValClear = param.getParamValue().toString().replace("\n","").replace("\t","");
                if(JSON.isValidArray(paramValClear)){
                    param.setParamValue(JSON.parseArray(paramValClear));
                }else if(JSON.isValid(paramVal) && paramVal.startsWith("{") && paramVal.endsWith("}")){
                    param.setParamValue(JSON.parseObject(paramVal));
                }else if(ObjectOF.equals_.test(param.getParamClassName(),Object.class.getName())){
                    // Objects dealing ignore.
                }else {
                    //Base Type.
                    param.setParamValue(paramVal.trim());
                    if(Strings.EMPTY.equals(param.getParamValue())){
                        param.setParamValue(null); continue;
                    }
                    Object baseTypeTrans = FieldConvertUtil.TransSupportJavaType.tryConstructSupportTransObject(paramCls, param.getParamValue());
                    param.setParamValue(baseTypeTrans);
                }
            }
        }
        //序列化与反序列化
        RunningInvoking.InvokerBucket invokerBucket = JSON.parseObject(JSON.toJSONString(clsMetadata), RunningInvoking.InvokerBucket.class);

        //执行负载
        if(clsMetadata.getStrategy() == 1 && clusterModeServerIdentityTree != null){
            ExecuteMetadata.ClusterExecuteResult clusterExecuteResult = new ExecuteMetadata.ClusterExecuteResult();
            List<ExecuteMetadata.ExecuteResult> nodes = clusterExecuteResult.getNode();
            Set<String> balancerNodes = clusterModeServerIdentityTree.apply(appName);
            for (String balancerNodeIdentity : balancerNodes) {
                RequestEntity balancerRequestEntity = requestEntitySupplier.apply(Boolean.TRUE,invokerBucket, balancerNodeIdentity);
                ExecuteMetadata.ExecuteResult balancerExecutor = execute(balancerRequestEntity, balancerNodeIdentity);
                if(!balancerExecutor.getSuccess()){ clusterExecuteResult.setSuccess(Boolean.FALSE); }
                nodes.add(balancerExecutor);
            }
            return clusterExecuteResult;
        }
        //执行策略 0 本地, 1 负载的实例
        else {
            //发送本地请求 测试接口连通性.
            RequestEntity localRequestEntity = requestEntitySupplier.apply(Boolean.FALSE,invokerBucket);
            ExecuteMetadata.ExecuteResult localRes = execute(localRequestEntity,localNode);
            return localRes;
        }
    }


    //请求执行.
    private ExecuteMetadata.ExecuteResult execute(RequestEntity requestEntity,String nodeIdentity){
        ExecuteMetadata.ExecuteResult executeResult = new ExecuteMetadata.ExecuteResult();
        executeResult.setHostPort(nodeIdentity);
        Object[] result = (Object[]) AryOF.create_.apply(Object.class, 2);
        result[0] = Boolean.TRUE; result[1] = ObjectOF.getNull_.get();
        JSONObject jsonResponse = (JSONObject)ObjectOF.getNull_.get();
        try {
            ResponseEntity<JSONObject> response = restTemplate.exchange(requestEntity, JSONObject.class);
            jsonResponse = response.getBody();
            if(jsonResponse != null && ObjectOF.notNull_.test(jsonResponse.get("error_code"))){
                executeResult.setSuccess(Boolean.FALSE);
            }
            executeResult.setResult(jsonResponse.get("invoke_res"));
        }catch (Throwable e){ executeResult.setSuccess(Boolean.FALSE); executeResult.setResult(String.format(SERVER_API_ERROR,e.getMessage())); }
        return executeResult;
    }

    //请求Entity构造. FALSE -> LOCAL, TRUE -> CLUSTER.
    private HandleSupplier.FunctionAry<RequestEntity<JSONObject>> requestEntitySupplier = (strategy_body_nodeIdentity) -> {
        if(strategy_body_nodeIdentity[0].equals(Boolean.FALSE)){
            return new RequestEntity(strategy_body_nodeIdentity[1],postHandlers,HttpMethod.POST, localAppURI);
        }else {
            try { return new RequestEntity(strategy_body_nodeIdentity[1],postHandlers,HttpMethod.POST, new URI(String.format(URI_TEMPLATE,strategy_body_nodeIdentity[2])));
            } catch (Throwable e) { /*Make Sure No exception.*/}
            return null;
        }
    };

    //组织参数表列,忽略即可.
    private List<ExecuteMetadata.ParamMetadata> digestParams(Parameter[] parameters, String[] pDocs){
        if(parameters.length > 0){
            List<ExecuteMetadata.ParamMetadata> params = new ArrayList<ExecuteMetadata.ParamMetadata>(parameters.length);
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                ExecuteMetadata.ParamMetadata paramUnit = new ExecuteMetadata.ParamMetadata();
                paramUnit.setParamClassName(parameter.getType().getName());
                paramUnit.setParamIdx(i);
                if(pDocs != null && pDocs.length >= i+1) paramUnit.setParamDoc(pDocs[i]);
                params.add(paramUnit);
            }
            return params;
        }
        return null;
    }


}
